﻿// ******************************************************************************
// Developed by Oguz Demirel
// Date: April 5, 2010
// ******************************************************************************
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.ApplicationPages;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;

namespace BulkListActions
{
    public partial class UpdateMetadata : WebAdminPageBase
    {
        #region ..:: FIELDS ::..

        protected Literal litMessages;
        protected Panel pnlMetadata;
        protected Panel inputForm;
        protected Button btnSave;
        protected Button btnCancel;

        private const string MAIN_TABLE_ID = "tblMain";
        private const string LABEL = "lbl{0}";
        private const string FIELD = "fld{0}";
        private const string CHECK = "cb{0}";
        private const string DROPDOWN = "ddl{0}";
        private const string LABEL_CSS = "ms-formlabel";
        private const string FIELD_CSS = "ms-formbody";

        string[] IDs;
        string[] fields;
        string listID;
        string referrer;
        string webID;
        string webUrl;
        string listUrl;

        // Create a dictionary to hold field keys and values
        Dictionary<string, string> commonfields = new Dictionary<string, string>();
        #endregion ..:: FIELDS ::..

        #region ..:: PAGE EVENTS ::..
        protected override void OnInit(EventArgs e)
        {
            // Get query string values
            string qsMetadataFields = Request.QueryString["meta"];
            string qsItemIDs = Request.QueryString["IDs"];
            listID = Request.QueryString["ListID"];
            referrer = Request.QueryString["referrer"];

            // Find web ID and url of referrer web
            using (SPSite site = new SPSite(referrer))
            {
                SPWebCollection webs = site.AllWebs;
                foreach (SPWeb webObj in webs)
                    if (webObj.Url == referrer)
                    {
                        webID = webObj.ID.ToString();
                        webUrl = webObj.Url;
                        listUrl = webObj.Lists[new Guid(listID)].DefaultViewUrl;
                        break;
                    }
            }

            // Get selected item IDs
            IDs = qsItemIDs.Split(new char[] { '_' });
            // Get metadata fields to be updated
            fields = qsMetadataFields.Split(new char[] { ' ' });

            // Check if IDs exist
            using (SPSite site = new SPSite(referrer))
            using (SPWeb web = site.OpenWeb(new Guid(webID)))
            {
                SPList list = web.Lists[new Guid(listID)];
                foreach (string id in IDs)
                    try
                    {
                        if (list.GetItemByUniqueId(new Guid(id)) != null)
                            continue;

                    }
                    catch
                    {
                        SPUtility.TransferToErrorPage("Error occurred: List item doesn't exist. {0} {1}", "Click here to go back to the list.", referrer);
                    }
            }

            // Create controls based on common metadata of selected items
            CreateContols();
        }

        protected override void OnLoad(EventArgs e)
        {

            //**************************************** 
            // Validate the page request to avoid 
            // any malicious posts 
            if (Request.HttpMethod == "POST")
                SPUtility.ValidateFormDigest();

            //**************************************** 
            // Validate the page request to avoid 
            // any malicious posts 
            if (Request.HttpMethod == "POST")
                SPUtility.ValidateFormDigest();

            //****************************************
            // Initialize the controls on the page
            // if its not a PostBack request
            if (!(Page.IsPostBack))
            {

            } // end - ! IsPostPack 

        }

        //*************************************************************
        // This method is called when the user clicks the "OK" button
        protected void BtnSubmit_Click(object sender, EventArgs e)
        {
            try
            {
                // Get metadata html table from panel
                Table table = this.pnlMetadata.FindControl(MAIN_TABLE_ID) as Table;
                // Save values provided by user
                SaveControlValues(table);

                // ********************************************************
                // Update selected items metadata with provided values
                // ********************************************************

                using (SPSite site = new SPSite(referrer))
                {
                    // We need to work on exact referrer web obj.
                    // That's why we use here web id, since this web could 
                    // be a subsite as well
                    using (SPWeb web = site.OpenWeb(new Guid(webID)))
                    {
                        web.AllowUnsafeUpdates = true;
                        // Get list
                        SPList list = web.Lists[new Guid(listID)];

                        // Loop thru all selected items
                        foreach (var itemID in IDs)
                        {
                            // Get item
                            SPListItem item = list.Items[new Guid(itemID)];

                            // Checkout item if necessary
                            if (list.ForceCheckout && item.File.CheckOutStatus == SPFile.SPCheckOutStatus.None)
                                item.File.CheckOut();

                            // Loop thru common metadata fields
                            foreach (KeyValuePair<string, string> field in commonfields)
                                // Assign new values to metadata field
                                UpdateItemWithNewMetadata(web, item, field);

                            // Apply changes
                            item.Update();

                            // Checkin item if necessary
                            if (list.ForceCheckout && item.File.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                                item.File.CheckIn("Checked-in by Bulk Update Metadata action.");

                        } // end - foreach items

                        web.AllowUnsafeUpdates = false;

                    } // end - using Web

                } // end - using Site

                Response.Redirect(listUrl);

            } // end - try
            catch (Exception ex)
            {
                // if an error occurs, capture the message to display it to the user
                throw new Exception("Error while updating metadata: " + ex.Message);
            }

        }

        //*************************************************************
        // This method is called when the user clicks the "Cancel" button
        protected void BtnCancel_Click(object sender, EventArgs e)
        {
            // Redirect back to list
            Response.Redirect(listUrl);
        }
        #endregion ..:: PAGE EVENTS ::..

        #region ..:: PRIVATE METHODS ::..
        // This function creates dynamically created controls 
        private void CreateContols()
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                using (SPSite site = new SPSite(referrer))
                {
                    using (SPWeb web = site.OpenWeb(new Guid(webID)))
                    {
                        SPList list = web.Lists[new Guid(listID)];

                        // *****************************************************************
                        // Dynamic Form Generation Logic
                        // *****************************************************************
                        // Create empty table
                        Table table = new Table();
                        table.CellPadding = 0;
                        table.CellSpacing = 0;
                        table.ID = MAIN_TABLE_ID;

                        #region .. Grid Header ..
                        // Table header row
                        TableRow headerRow = new TableRow();
                        table.Rows.Add(headerRow);
                        // header cell
                        TableCell cellHeader1 = new TableCell();
                        TableCell cellHeader2 = new TableCell();
                        TableCell cellHeader3 = new TableCell();
                        TableCell cellHeader4 = new TableCell();

                        // Labels for header text
                        Label lblCol1 = new Label();
                        lblCol1.ID = "lblInclude";
                        lblCol1.Text = "Select";
                        lblCol1.CssClass = "ms-smallheader";
                        cellHeader1.Controls.Add(lblCol1);
                        headerRow.Cells.Add(cellHeader1);

                        // Labels for header text
                        Label lblCol2 = new Label();
                        lblCol2.ID = "lblField";
                        lblCol2.Text = "";
                        lblCol2.CssClass = "ms-smallheader";
                        cellHeader2.Controls.Add(lblCol2);
                        headerRow.Cells.Add(cellHeader2);

                        // Labels for header text
                        Label lblCol3 = new Label();
                        lblCol3.ID = "lblValue";
                        lblCol3.Text = "New value";
                        lblCol3.CssClass = "ms-smallheader";
                        cellHeader3.Controls.Add(lblCol3);
                        headerRow.Cells.Add(cellHeader3);

                        // Labels for header text
                        Label lblCol4 = new Label();
                        lblCol4.ID = "lblDDL";
                        lblCol4.Text = "Existing values";
                        lblCol4.CssClass = "ms-smallheader";
                        cellHeader4.Controls.Add(lblCol4);
                        headerRow.Cells.Add(cellHeader4);
                        #endregion .. Grid Header ..

                        foreach (string fieldName in fields)
                        {
                            // Get field
                            SPField field = list.Fields.GetField(fieldName);
                            // Get first item as primary item
                            SPListItem item = list.Items[new Guid(IDs[0])];
                            // Create a new context
                            SPContext newContext = SPContext.GetContext(HttpContext.Current,
                                    item.ID, new Guid(listID), web);

                            #region .. Grid Controls ..
                            // ***********************************************************
                            // Controls: Checkbox, FieldLabel, FieldControl, Dropdown list
                            // ***********************************************************

                            // Checkbox to select which fields 
                            // metadata updates will be applied to
                            CheckBox cb = new CheckBox();
                            cb.ID = String.Format(CHECK, field.InternalName);

                            // Consturct field label object and assing its context
                            FieldLabel fieldLabel = new FieldLabel();
                            fieldLabel.ControlMode = SPControlMode.Edit;
                            fieldLabel.ItemId = item.ID;
                            fieldLabel.ListId = list.ID;
                            fieldLabel.FieldName = field.InternalName;
                            fieldLabel.ItemContext = newContext;
                            fieldLabel.RenderContext = newContext;
                            fieldLabel.ID = String.Format(LABEL, field.InternalName);

                            // Consturct form field object and assing its context
                            FormField formField = new FormField();
                            formField.ControlMode = SPControlMode.Edit;
                            formField.ItemId = item.ID;
                            formField.ListId = list.ID;
                            formField.FieldName = field.InternalName;
                            formField.ItemContext = newContext;
                            formField.RenderContext = newContext;
                            formField.ID = String.Format(FIELD, field.InternalName);

                            DropDownList ddl = new DropDownList();
                            ddl.ID = String.Format(DROPDOWN, field.InternalName);
                            ddl.Items.Add("Select from existing values...");

                            // Loop thru all selected items and 
                            // add existing values to ddl
                            foreach (var id in IDs)
                            {
                                // Get item
                                SPListItem listitem = list.Items[new Guid(id)];
                                // Add existing values to drop down list
                                if (listitem[fieldName] != null)
                                    ddl.Items.Add(listitem[fieldName].ToString());
                            }
                            // Remove duplicate values in ddl
                            RemoveDuplicateItems(ddl);
                            // Reset selection to first item
                            ddl.SelectedIndex = 0;

                            // *********************************************************
                            TableRow row = new TableRow();
                            table.Rows.Add(row);

                            TableCell cellCheckbox = new TableCell();
                            TableCell cellLabel = new TableCell();
                            TableCell cellControl = new TableCell();
                            TableCell cellDropdown = new TableCell();

                            cellCheckbox.Controls.Add(cb);
                            cellLabel.Controls.Add(fieldLabel);
                            cellControl.Controls.Add(formField);
                            cellDropdown.Controls.Add(ddl);

                            cellCheckbox.CssClass = LABEL_CSS;
                            cellLabel.CssClass = LABEL_CSS;
                            cellControl.CssClass = FIELD_CSS;
                            cellDropdown.CssClass = FIELD_CSS;

                            row.Cells.Add(cellCheckbox);
                            row.Cells.Add(cellLabel);
                            row.Cells.Add(cellControl);
                            row.Cells.Add(cellDropdown);
                            #endregion .. Grid Controls ..

                        } // end - foreach

                        // Add dynamically generated html table to panel control
                        this.pnlMetadata.Controls.Add(table);

                    } // end - using SPWeb

                } // end - using SPSite

            }); // end - RunWithElevatedPrivileges

        } // end - CreateControls

        // This function saves values of dynamically created controls 
        private void SaveControlValues(Table table)
        {
            if (table != null)
            {   // Loop for each row in metadata table
                foreach (TableRow row in table.Rows)
                {
                    // Flag to include current row or not
                    // Only include if checkbok is checked
                    bool include = false;
                    // internal name of the field
                    string internalName = string.Empty;

                    // Loop for each cell in metadata table
                    foreach (TableCell cell in row.Cells)
                    {
                        // Loop for each control in metadata table
                        foreach (Control control in cell.Controls)
                        {
                            if (control is CheckBox)
                                if (((CheckBox)control).Checked)
                                    include = true;

                            #region .. FormField ..
                            if (control is FormField && include)
                            {
                                FormField field = control as FormField;

                                // Field existence check
                                if (field != null && field.Field != null && !commonfields.ContainsKey(field.FieldName))
                                {
                                    // Assign internal name
                                    internalName = field.FieldName;

                                    // ********************************************************************
                                    // User field update
                                    // ********************************************************************
                                    if (field.Field.Type == SPFieldType.User)
                                    {
                                        SPUser user = null;
                                        string userLoginName = string.Empty;

                                        if (field.Value == null || ((string)field.Value == string.Empty))
                                        {
                                            // Don't do anything, since there is nothing to update
                                        }
                                        else
                                        {
                                            using (SPSite homeSite = new SPSite(SPContext.Current.Site.Url))
                                            {
                                                using (SPWeb homeWeb = homeSite.OpenWeb(SPContext.Current.Web.ID))
                                                {
                                                    Regex rg = new Regex(";#");
                                                    string[] userValues = rg.Split(System.Convert.ToString(field.Value));

                                                    if (userValues.Length > 1)
                                                    {
                                                        // Loop for all user values provided
                                                        for (int i = 0; i < userValues.Length; i++)
                                                        {
                                                            // Pick only odd numbered indexes which are user name values
                                                            if (i % 2 == 1)
                                                            {
                                                                userLoginName += userValues[i] + ";";
                                                            }
                                                        }
                                                    }
                                                    else if (userValues.Length == 1)
                                                    {
                                                        user = homeWeb.AllUsers.GetByID(Convert.ToInt32(userValues[0]));
                                                        userLoginName = user.LoginName;
                                                    }
                                                } // end - using SPWeb
                                            } // end - using SPSite
                                        } // end - else

                                        if (userLoginName != string.Empty)
                                        {
                                            commonfields.Add(field.FieldName, userLoginName);
                                        }
                                        else
                                        {
                                            commonfields.Add(field.FieldName, string.Empty);
                                        }

                                    }
                                    else
                                    {
                                        // other fields update
                                        commonfields.Add(field.FieldName, Convert.ToString(field.Value));
                                    }
                                } // END - Field existence check

                            } // if control is field
                            #endregion .. FormField ..

                            #region .. Dropdown ..
                            if (control is DropDownList)
                            {
                                DropDownList ddl = control as DropDownList;
                                // If user selected from existing values
                                if (ddl.SelectedIndex != 0)
                                    commonfields[internalName] = ddl.SelectedValue;
                            }
                            #endregion .. Dropdown ..

                        } // END - Loop for each control in metadata table
                    } // END - Loop for each cell in metadata table
                } // END - Loop for each row in metadata table
            }

        }

        // This function assigns new metadata to item
        private void UpdateItemWithNewMetadata(SPWeb web, SPListItem item, KeyValuePair<string, string> kvp)
        {

            // User fields 
            if (item.Fields.GetFieldByInternalName(kvp.Key).Type == SPFieldType.User)
            {
                if (!string.IsNullOrEmpty(kvp.Value))
                {
                    // Multiple user values provided
                    if (kvp.Value.Contains(";"))
                    {
                        SPFieldUserValueCollection usercollection = new SPFieldUserValueCollection();
                        string[] userarray = kvp.Value.Split(';');
                        for (int i = 0; i < userarray.Length - 1; i++)
                        {
                            SPFieldUserValue usertoadd = this.ConvertLoginName(web, userarray[i]);
                            usercollection.Add(usertoadd);
                        }

                        item[kvp.Key] = usercollection;
                    }
                    else
                    {
                        SPUser user = web.EnsureUser(kvp.Value);
                        item[kvp.Key] = user;
                    }
                }
                else
                {
                    item[kvp.Key] = string.Empty;
                }
            }
            else if (item.Fields.GetFieldByInternalName(kvp.Key).Type == SPFieldType.DateTime)
            {
                // DateTime fields 
                if (!string.IsNullOrEmpty(kvp.Value))
                {
                    item[kvp.Key] = Convert.ToDateTime(kvp.Value);
                }
                else
                {
                    item[kvp.Key] = null;
                }
            }
            else
            {
                // other fields 
                item[kvp.Key] = kvp.Value;
            }

        }

        private SPFieldUserValue ConvertLoginName(SPWeb web, string userid)
        {
            SPUser requireduser = web.EnsureUser(userid);
            SPFieldUserValue uservalue = new SPFieldUserValue(web, requireduser.ID, requireduser.LoginName);
            return uservalue;
        }

        private void RemoveDuplicateItems(DropDownList ddl) 
        { 
            for (int i = 0; i < ddl.Items.Count; i++) 
            { 
                ddl.SelectedIndex = i; 
                string str = ddl.SelectedItem.ToString(); 
                for (int counter = i + 1; counter < ddl.Items.Count; counter++) 
                { 
                    ddl.SelectedIndex = counter; 
                    string compareStr = ddl.SelectedItem.ToString(); 
                    if (str == compareStr) 
                    { 
                        ddl.Items.RemoveAt(counter); 
                        counter = counter - 1; 
                    } 
                } 
            } 
        }
        #endregion ..:: PRIVATE METHODS ::..

    }
}
